import CONSTANT from "../constant";

export default class Quat {
    constructor(x=0,y=0,z=0,w=1) {
        this.elements=new Float64Array(4);
        this.elements[0]=x;
        this.elements[1]=y;
        this.elements[2]=z;
        this.elements[3]=w;
        CONSTANT.QUAT_NUM_CREATIONS++;
    }
    get x(){
        return this.elements[0];
    }
    set x(num){
        this.elements[0]=num;
    }
    get y(){
        return this.elements[1];
    }
    set y(num){
        this.elements[1]=num;
    }
    get z(){
        return this.elements[1];
    }
    set z(num){
        this.elements[1]=num;
    }
    get w(){
        return this.elements[1];
    }
    set w(num){
        this.elements[1]=num;
    }


    identity() {
        let es=this.elements;
        es[0] = es[1] = es[2] = 0;
        es[3] = 1;
        return this;
    }
    init(x,y,z,w) {
        let es=this.elements;
        es[0] = x;
        es[1] = y;
        es[2] = z;
        es[3] = w;
        return this;
    }
    add(q) {
        return new Quat(this.x + q.x,this.y + q.y,this.z + q.z,this.w + q.w);
    }
    sub(q) {
        return new Quat(this.x - q.x,this.y - q.y,this.z - q.z,this.w - q.w);
    }
    scale(s) {
        return new Quat(this.x * s,this.y * s,this.z * s,this.w * s);
    }
    length() {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
    }
    lengthSq() {
        return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
    }
    dot(q) {
        return this.x * q.x + this.y * q.y + this.z * q.z + this.w * q.w;
    }
    normalized() {
        let invLen = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
        if(invLen > 0) {
            invLen = 1 / invLen;
        }
        return new Quat(this.x * invLen,this.y * invLen,this.z * invLen,this.w * invLen);
    }
    normalize() {
        let invLen = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
        if(invLen > 0) {
            invLen = 1 / invLen;
        }
        this.x *= invLen;
        this.y *= invLen;
        this.z *= invLen;
        this.w *= invLen;
        return this;
    }
    slerp(q,t) {
        let qx;
        let qy;
        let qz;
        let qw;
        let d = this.x * q.x + this.y * q.y + this.z * q.z + this.w * q.w;
        if(d < 0) {
            d = -d;
            qx = -q.x;
            qy = -q.y;
            qz = -q.z;
            qw = -q.w;
        } else {
            qx = q.x;
            qy = q.y;
            qz = q.z;
            qw = q.w;
        }
        if(d > 0.999999) {
            let _this = new Quat(this.x + (qx - this.x) * t,this.y + (qy - this.y) * t,this.z + (qz - this.z) * t,this.w + (qw - this.w) * t);
            let invLen = Math.sqrt(_this.x * _this.x + _this.y * _this.y + _this.z * _this.z + _this.w * _this.w);
            if(invLen > 0) {
                invLen = 1 / invLen;
            }
            _this.x *= invLen;
            _this.y *= invLen;
            _this.z *= invLen;
            _this.w *= invLen;
            return _this;
        }
        let theta = t * Math.acos(d);
        qx -= this.x * d;
        qy -= this.y * d;
        qz -= this.z * d;
        qw -= this.w * d;
        let invLen = 1 / Math.sqrt(qx * qx + qy * qy + qz * qz + qw * qw);
        qx *= invLen;
        qy *= invLen;
        qz *= invLen;
        qw *= invLen;
        let sin = Math.sin(theta);
        let cos = Math.cos(theta);
        return new Quat(this.x * cos + qx * sin,this.y * cos + qy * sin,this.z * cos + qz * sin,this.w * cos + qw * sin);
    }
    clone() {
        return new Quat(this.x,this.y,this.z,this.w);
    }
    toString() {
        return "Quat[" + (this.x > 0 ? (this.x * 10000000 + 0.5 | 0) / 10000000 : (this.x * 10000000 - 0.5 | 0) / 10000000) + " i,\n" + "    " + (this.y > 0 ? (this.y * 10000000 + 0.5 | 0) / 10000000 : (this.y * 10000000 - 0.5 | 0) / 10000000) + " j,\n" + "    " + (this.z > 0 ? (this.z * 10000000 + 0.5 | 0) / 10000000 : (this.z * 10000000 - 0.5 | 0) / 10000000) + " k,\n" + "    " + (this.w > 0 ? (this.w * 10000000 + 0.5 | 0) / 10000000 : (this.w * 10000000 - 0.5 | 0) / 10000000) + "]";
    }
}